package api_g

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"
)

var g_GoPackageName = "" // go.mod中的包名(从项目根目录中读取)
const (
	DEF_OUT_PACKAGE          = "api"             // 生成的API接口包的包名
	DEF_OUT_API_INIT_FILE    = "/api/init.go"    // 生成的API接口包的init文件路径
	DEF_OUT_INIT_FILE        = "init.go"         // 每个API路由组包的init文件
	DEF_OUT_HAND_FILE        = "service.go"      // 每个API路由组的所有接口输出到一个.go文件
	DEF_OUT_SERVER_INIT_FILE = "/server/init.go" // 生成的rpc server 的代码自动注册init文件
)

type ProtoInfo struct {
	FileName string                 // 文件名(含后缀)  xxx.proto
	Project  string                 // 项目名称
	Service  string                 // 服务名称 文件内的 time
	Package  string                 // 包名 proto/pb
	Messages map[string][]string    // 本文件中的所有结构名
	Servers  []*ServerInfo          // 每个 rpc server
	Serversm map[string]*ServerInfo // 每个方法与 rpc server 的反向映射
}
type ServerInfo struct {
	Name  string
	Rpcs  []*RpcInfo
	Rpcsm map[string]*RpcInfo
}
type RpcInfo struct {
	Title   string
	Func    string
	InReq   string
	OutResp string
}

func (this *ProtoInfo) TransApiFile(dir string) error {
	if len(this.Servers) == 0 {
		return nil
	}
	if err := this.OverideServerInitFile(dir); err != nil {
		return err
	}
	if outStr, err := GoFmtFile(true, "-w", dir+DEF_OUT_SERVER_INIT_FILE); err != nil {
		return errors.New(fmt.Sprintf("gofmt DEF_OUT_SERVER_INIT_FILE, 命令执行失败:%v with %s\n", err, outStr))
	}

	if err := this.OverideApiInitFile(); err != nil {
		return err
	}

	if err := this.OverideApiRouterFile(); err != nil {
		return err
	}
	if outStr, err := GoFmtFile(true, "-w", fmt.Sprintf("./api/%s/%s", this.Service, DEF_OUT_INIT_FILE)); err != nil {
		return errors.New(fmt.Sprintf("gofmt DEF_OUT_INIT_FILE, 命令执行失败:%v with %s\n", err, outStr))
	}

	if err := this.OverideApiServiceFile(); err != nil {
		return err
	}
	if outStr, err := GoFmtFile(true, "-w", fmt.Sprintf("./api/%s/%s", this.Service, DEF_OUT_HAND_FILE)); err != nil {
		return errors.New(fmt.Sprintf("gofmt DEF_OUT_HAND_FILE, 命令执行失败:%v with %s\n", err, outStr))
	}
	return nil
}
func (this *ProtoInfo) OverideApiInitFile() error {
	// 不存在此文件时先创建它
	fileIni := "./" + DEF_OUT_API_INIT_FILE
	_, err := os.Stat(fileIni) // "./api/init.go"
	if !(err == nil || os.IsExist(err)) {
		os.MkdirAll(filepath.Dir(fileIni), os.ModePerm)
		file, _ := os.OpenFile(fileIni, os.O_CREATE, 0666)
		file.WriteString(fmt.Sprintf(emptyApiInit, DEF_OUT_PACKAGE, time.Now().Format("2006-01-02")))
		file.Close()
	}

	// 修改文件
	f, err := os.Open(fileIni)
	if err != nil {
		return err
	}

	var templ_buf bytes.Buffer
	startImport, startInit := 0, 0
	for buf := bufio.NewReader(f); ; {
		line, err := buf.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				// line += "\n"
				// templ_buf.WriteString(line)
				break
			}
			return err
		}

		line_ := strings.TrimSpace(line)

		if startImport == 0 && strings.HasPrefix(line_, "import (") {
			startImport = 1
		}
		if startImport == 1 || startInit == 1 {
			// 清理
			fields := strings.Fields(line_)
			if len(fields) > 0 && fields[len(fields)-1] == this.Service {
				continue
			}
		}
		if strings.HasPrefix(line_, ")") {
			if startImport == 1 {
				startImport = 2
				// 写入import
				templ_buf.WriteString(fmt.Sprintf(importApiPackge, g_GoPackageName, this.Service, this.Service))
			}
		}
		templ_buf.WriteString(line)
	}
	f.Close()

	// 写调整后的
	f, err = os.OpenFile(fileIni, os.O_RDWR|os.O_TRUNC, 0666)
	if err != nil {
		return err
	}

	templ_buf.WriteTo(f)
	f.Close()
	return nil
}
func (this *ProtoInfo) OverideApiRouterFile() error {
	// 不存在此文件时先创建它
	routeFile := fmt.Sprintf("./api/%s/%s", this.Service, DEF_OUT_INIT_FILE)
	_, err := os.Stat(routeFile)
	if !(err == nil || os.IsExist(err)) {
		os.MkdirAll(filepath.Dir(routeFile), os.ModePerm)
		file, _ := os.OpenFile(routeFile, os.O_CREATE, 0666)
		file.WriteString(fmt.Sprintf(emptyRouterInit, this.Service, time.Now().Format("2006-01-02"), g_GoPackageName, this.Package))
		file.Close()
	}

	// 修改文件
	f, err := os.Open(routeFile)
	if err != nil {
		return err
	}

	var part1_buf, templ_buf bytes.Buffer // var xxx 以前的内容
	initStart, routerStart, modifyRouter, hasRegist, lastLine_, curSvc := false, false, false, false, "", ""
	hasServers := map[string]bool{}
	for buf := bufio.NewReader(f); ; {
		line, err := buf.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				// line += "\n"
				// templ_buf.WriteString(line)
				break
			}
			return err
		}
		line_ := strings.TrimSpace(line)

		// 清理 var clixxx
		if strings.HasPrefix(line_, "var cli") {
			fields := strings.Fields(line_)
			if fields[len(fields)-1] == this.FileName {
				continue
			}
		}

		// 开始处理注册
		if strings.HasPrefix(line_, "func init()") {
			part1_buf = templ_buf // 转交给 part1_buf
			templ_buf = bytes.Buffer{}
			initStart = true

			for _, it := range this.Servers {
				if _, ok := hasServers[titleCase(it.Name)]; ok {
					continue
				}
				if _, ok := hasServers[titleCase(it.Name)+"s"]; ok {
					continue
				}
				part1_buf.WriteString(fmt.Sprintf(globalClient, titleCase(it.Name), titleCase(it.Name), titleCase(it.Name), titleCase(it.Name), titleCase(it.Name)))
			}
		}

		if lastLine_ == "" && line_ == "" {
			continue
		}

		// 清理已存在的htp.Register( 和 rpc.RegistClient(
		fields := strings.Fields(line_)
		if len(fields) > 1 && fields[len(fields)-1] == this.FileName {
			continue
		}
		if strings.Contains(line_, "htp.Register(") {
			curRegist := strings.FieldsFunc(line_, func(r rune) bool {
				if r == ',' || r == '{' {
					return true
				}
				return false
			})[1]
			curRegist = strings.TrimSpace(curRegist)
			curSvc = curRegist[0:strings.Index(curRegist, "Router")]
			if curSvc == titleCase(this.Service) {
				hasRegist = true
			}
		}
		if strings.Contains(line_, "var cli") {
			curCli := strings.Fields(line_)[1]
			hasServers[string(curCli[3:])] = true
		}
		if strings.Contains(line_, "Init(") {
			routerStart = true
			curRouter := strings.FieldsFunc(line_, func(r rune) bool {
				if r == '(' || r == ')' {
					return true
				}
				return false
			})[1]
			curRouter = strings.TrimSpace(curRouter)
			curSvc = curRouter[0:strings.Index(curRouter, "Router")]
		}

		if strings.HasPrefix(line_, "}") {
			if initStart {
				initStart = false

				// 写入注册Router
				if !hasRegist {
					templ_buf.WriteString(fmt.Sprintf(registRoute, titleCase(this.Service)))
				}
				// 写入注册Client
				for _, it := range this.Servers {
					if _, ok := hasServers[titleCase(it.Name)]; ok {
						continue
					}
					if _, ok := hasServers[titleCase(it.Name)+"s"]; ok {
						continue
					}
					templ_buf.WriteString(fmt.Sprintf(registClient, this.Service, "cli"+titleCase(it.Name), titleCase(it.Name)))
				}
			}
			if routerStart {
				routerStart = false
				if curSvc == titleCase(this.Service) {
					modifyRouter = true
					templ_buf.WriteString("\n")
					for _, it := range this.Servers {
						// 写入路由接口
						if lastLine_ != "" {
							//	templ_buf.WriteString("\n")
						}
						templ_buf.WriteString(fmt.Sprintf(routerGroup, it.Name, it.Name, this.FileName))

						for _, rpc := range it.Rpcs {
							templ_buf.WriteString(fmt.Sprintf(registerAPI, it.Name, titleLetter(rpc.Func), titleCase(rpc.Func), this.FileName))
						}
						templ_buf.WriteString("\n")
					}
				}
			}
		}
		lastLine_ = line_
		templ_buf.WriteString(line)
	}
	// 写入 xxxRouter
	if !modifyRouter {
		var initRoute_buf bytes.Buffer // 写入路由接口
		for _, it := range this.Servers {
			initRoute_buf.WriteString(fmt.Sprintf(routerGroup, it.Name, it.Name, this.FileName))

			for _, rpc := range it.Rpcs {
				initRoute_buf.WriteString(fmt.Sprintf(registerAPI, it.Name, titleLetter(rpc.Func), titleCase(rpc.Func), this.FileName))
			}
		}

		templ_buf.WriteString(fmt.Sprintf(ServiceRouter, titleCase(this.Service),
			titleCase(this.Service), this.Service, titleCase(this.Service),
			initRoute_buf.String()))
		templ_buf.WriteString("\n")
	}

	f.Close()

	// 写调整后的
	f, err = os.OpenFile(routeFile, os.O_RDWR|os.O_TRUNC, 0666)
	if err != nil {
		return err
	}

	part1_buf.WriteTo(f)
	templ_buf.WriteTo(f)
	f.Close()

	return nil
}
func (this *ProtoInfo) OverideApiServiceFile() error {
	// 不存在此文件时先创建它
	fileHand := fmt.Sprintf("./api/%s/%s", this.Service, DEF_OUT_HAND_FILE)
	_, err := os.Stat(fileHand)
	if !(err == nil || os.IsExist(err)) {
		file, _ := os.OpenFile(fileHand, os.O_CREATE, 0666)
		file.WriteString(fmt.Sprintf(emptyHandle, this.Service, time.Now().Format("2006-01-02"), g_GoPackageName, this.Package))
		file.Close()
	}

	// 修改文件
	f, err := os.Open(fileHand)
	if err != nil {
		return err
	}

	var templ_buf bytes.Buffer
	cur_ser_rpc := []string{}
	valid, rpcValidates := false, map[string]string{}
	limited, rpcLimiters := false, map[string]string{}
	unsafed, rpcUnsafers := false, map[string]string{}
	for buf := bufio.NewReader(f); ; {
		line, err := buf.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				// line += "\n"
				// templ_buf.WriteString(line)
				break
			}
			return err
		}

		line_ := strings.TrimSpace(line)

		// 清理 原有接口
		if strings.HasPrefix(line_, "//=>") {
			fields := strings.Fields(line_)
			cur_ser_rpc = []string{}
			if fields[len(fields)-1] == this.FileName {
				cur_ser_rpc = strings.Split(fields[1], ".")
				continue
			}
		}

		if strings.HasPrefix(line_, "func (this") {
			if valid {
				valid = false
			}
			if limited {
				limited = false
			}
			if unsafed {
				unsafed = false
			}
		}

		if len(cur_ser_rpc) > 0 {

			if strings.Contains(line_, "Unsafe()") {
				rpcUnsafers[strings.Join(cur_ser_rpc, ".")] = line
				unsafed = true
			} else if unsafed {
				rpcUnsafers[strings.Join(cur_ser_rpc, ".")] += line
			}

			if strings.Contains(line_, "Validate()") {
				rpcValidates[strings.Join(cur_ser_rpc, ".")] = line
				valid = true
			} else if valid {
				rpcValidates[strings.Join(cur_ser_rpc, ".")] += line
			}

			if strings.Contains(line_, "LimitParam()") {
				rpcLimiters[strings.Join(cur_ser_rpc, ".")] = line
				limited = true
			} else if limited {
				rpcLimiters[strings.Join(cur_ser_rpc, ".")] += line
			}
		}

		if len(cur_ser_rpc) > 0 {
			continue
		}

		templ_buf.WriteString(line)
	}
	f.Close()

	// 写入接口实现
	for _, it := range this.Servers {
		for _, rpc := range it.Rpcs {
			// 写入struct
			templ_buf.WriteString(fmt.Sprintf(apiHandStruct,
				it.Name, rpc.Func, this.FileName, titleCase(rpc.Func), titleCaseParam(rpc.InReq), titleCase(rpc.Func),
				titleCase(rpc.Func), rpc.Title, titleCaseParam(rpc.InReq), strings.Join(this.Messages[rpc.InReq], ","),
				titleCaseParam(rpc.OutResp), strings.Join(this.Messages[rpc.OutResp], ","),
				titleCaseParam(rpc.InReq), titleCaseParam(rpc.OutResp)))

			// 写入unsafe
			if unsafefun, ok := rpcUnsafers[it.Name+"."+rpc.Func]; ok {
				templ_buf.WriteString(unsafefun)
			} else {
				templ_buf.WriteString(fmt.Sprintf(apiHandUnsaf, titleCase(rpc.Func)))
			}

			// 写入validate
			if validfun, ok := rpcValidates[it.Name+"."+rpc.Func]; ok {
				templ_buf.WriteString(validfun)
			} else {
				templ_buf.WriteString(fmt.Sprintf(apiHandValid, titleCase(rpc.Func)))
			}

			// 写入limiter
			if limitfun, ok := rpcLimiters[it.Name+"."+rpc.Func]; ok {
				templ_buf.WriteString(limitfun)
			} else {
				templ_buf.WriteString(fmt.Sprintf(apiHandLimit, titleCase(rpc.Func)))
			}

			// 写入Handler
			templ_buf.WriteString(fmt.Sprintf(apiHandle,
				titleCase(rpc.Func), titleCase(it.Name), it.Name, titleCase(rpc.Func), titleCase(strings.TrimPrefix(rpc.InReq, "comm."))))
		}
	}

	// 写调整后的
	f, err = os.OpenFile(fileHand, os.O_RDWR|os.O_TRUNC, 0666)
	if err != nil {
		return err
	}

	templ_buf.WriteTo(f)
	f.Close()

	return nil
}
func (this *ProtoInfo) OverideServerInitFile(dir string) error {
	_, err := os.Stat(dir + DEF_OUT_SERVER_INIT_FILE) // "server/init.go"

	// 不存在此文件时先创建它 services/service/ server/init.go
	if !(err == nil || os.IsExist(err)) {
		os.MkdirAll(filepath.Dir(dir+DEF_OUT_SERVER_INIT_FILE), os.ModePerm)
		file, _ := os.OpenFile(dir+DEF_OUT_SERVER_INIT_FILE, os.O_CREATE, 0666)
		file.WriteString(fmt.Sprintf(emptySerInit, "server", time.Now().Format("2006-01-02")))
		file.Close()
	}

	// 如果xxx目录不存在则创建xxx包 services/service/server/ xxxxx/api.go
	fileBaseName := strings.Split(this.FileName, ".")[0]
	_, err_ := os.Stat(fmt.Sprintf(dir+"/server/%s/api.go", fileBaseName)) // 一个服务一个目录/api.go
	if !(err_ == nil || os.IsExist(err_)) {
		os.MkdirAll(filepath.Dir(fmt.Sprintf(dir+"/server/%s/api.go", fileBaseName)), os.ModePerm)
		file, _ := os.OpenFile(fmt.Sprintf(dir+"/server/%s/api.go", fileBaseName), os.O_CREATE, 0666)
		file.WriteString(fmt.Sprintf(emptySerMain, fileBaseName, time.Now().Format("2006-01-02"), fileBaseName, g_GoPackageName,
			this.Package, titleCase(fileBaseName), titleCase(fileBaseName)))
		file.Close()
	}

	// 修改文件 service/ server/init.go
	f, err := os.Open(dir + DEF_OUT_SERVER_INIT_FILE)
	if err != nil {
		return err
	}

	var templ_buf bytes.Buffer
	startImport, startInit, curGSName := 0, 0, ""
	for buf := bufio.NewReader(f); ; {
		line, err := buf.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				// line += "\n"
				// templ_buf.WriteString(line)
				break
			}
			return err
		}

		line_ := strings.TrimSpace(line)

		if startImport == 0 && strings.HasPrefix(line_, "import (") {
			startImport = 1
		}
		if startInit == 0 && strings.HasPrefix(line_, "func init() {") {
			startInit = 1
		}
		if startImport == 1 || startInit == 1 {
			// 清理
			fields := strings.Fields(line_)
			if len(fields) > 0 && fields[len(fields)-1] == this.FileName {
				if startInit == 1 {
					curField := strings.FieldsFunc(line_, func(r rune) bool {
						if r == '(' || r == ',' {
							return true
						}
						return false
					})[1]
					curGSName = strings.TrimSpace(curField)
				}
				continue
			}
		}
		if strings.HasPrefix(line_, ")") {
			if startImport == 1 {
				startImport = 2
				// 写入import
				templ_buf.WriteString(fmt.Sprintf(importPackge, g_GoPackageName, this.Service, strings.Split(this.FileName, ".")[0], this.FileName))
			}
		}
		if strings.HasPrefix(line_, "}") {
			if startInit == 1 {
				startInit = 2
				_curHtpSer := curGSName
				if _curHtpSer == "" {
					_curHtpSer = "HTP_NAME"
				}
				// 写入注册server
				for _, it := range this.Servers {
					templ_buf.WriteString(fmt.Sprintf(registServer, _curHtpSer, it.Name, it.Name, this.FileName))
				}
			}
		}
		templ_buf.WriteString(line)
	}
	f.Close()

	// 写调整后的
	f, err = os.OpenFile(dir+DEF_OUT_SERVER_INIT_FILE, os.O_RDWR|os.O_TRUNC, 0666)
	if err != nil {
		return err
	}

	templ_buf.WriteTo(f)
	f.Close()
	return nil
}
func titleCase(s string) string {
	return strings.ToUpper(string(s[0])) + s[1:]
}
func titleLetter(s string) string {
	return strings.ToLower(string(s[0])) + s[1:]
}

func titleCaseParam(s string) string {
	if strings.HasPrefix(s, "comm.") {
		s = strings.TrimPrefix(s, "comm.")
		return "c." + titleCase(s)
	}
	return "." + titleCase(s)
}

//------------------------------------------------------------------------------
var emptyApiInit = `package %s

/*******************************************************************************
Copyright:cloud
Author:cloudapex@126.com
Version:2.0
Date:%s
Description: 汇总所有Api接口
# !!!文件自动生成，不要手动修改!!!
*******************************************************************************/
import (
)

`
var importApiPackge = `	_ "%s/api/%s" // from %s
`

var emptyRouterInit = `package %s

/*******************************************************************************
Copyright:cloud
Author:cloudapex@126.com
Version:2.0
Date:%s
Description:微服务转API接口(供网关import)
# !!!文件自动生成，不要手动修改!!!
*******************************************************************************/
import (
	"gitee.com/cloudapex/ulern/htp"
	"gitee.com/cloudapex/ulern/rpc"
	"%s/%s"
	"github.com/gin-gonic/gin"
	"google.golang.org/grpc"
)

func init() {

}
`

var ServiceRouter = `
type %sRouter struct{ }

func (%sRouter) Name() string { return %q }

func (%sRouter) Init(ser string, r *gin.RouterGroup) {

%s
}
`

var globalClient = `var cli%s pb.%sClient

func Cli%s(c *gin.Context) pb.%sClient {
	return cli%s
}
`
var registRoute = `	htp.Register("Gate", %sRouter{})
`
var registClient = `	rpc.RegistClient("%s", func(endpoint string, cc *grpc.ClientConn) { %s = pb.New%sClient(cc) })
`

var routerGroup = `	_%s := r.Group("%s") // from %s
`

var registerAPI = `	htp.API(ser, _%s, "%s", &%s{}) // from %s
`

//------------------------------------------------------------------------------
var emptyHandle = `package %s

/*******************************************************************************
Copyright:cloud
Author:cloudapex@126.com
Version:2.0
Date:%s
Description:微服务转API接口(供网关import)
# !!!Unsafe LimitParam Validate 允许修改!!!
*******************************************************************************/
import (
	"context"
	"fmt"

	"gitee.com/cloudapex/ulern/htp"
	//"gitee.com/cloudapex/ulern/util"
	pbc "gitee.com/cloudapex/uservice/biz/proto/pb"
	"%s/%s"
	"github.com/gin-gonic/gin"
)

var _ = pbc.Request{}
`
var apiHandStruct = `//=> %s.%s from %s
type %s struct {
	pb%s
}

func (this *%s) Mode() htp.ESMode{ return htp.ES_Tran } // don't override

func (this *%s) Document(t htp.EAPIDoc) string { // don't override
	switch t {
	case htp.EADoc_Title:
		return %q
	case htp.EADoc_ParamIn:
		return htp.MarshalRawWithNode(pb%s{}, "json", []string{%s})
	case htp.EADoc_ParamOut:
		return htp.MarshalRawWithNode(pb%s{}, "json", []string{%s})
	case htp.EADoc_CaseIn:
		return htp.MarshalJsonWithZero(pb%s{}, "json")
	case htp.EADoc_CaseOut:
		return htp.MarshalJsonWithZero(pb%s{}, "json")
	}
	return fmt.Sprintf("Unknown EAPIDoc type")
}

`
var apiHandUnsaf = `func (this *%s) Unsafe() bool { return false } // can override

`
var apiHandLimit = `func (this *%s) LimitParam() []int { return nil } // can override

`
var apiHandValid = `func (this *%s) Validate() error { return nil } // can override

`

var apiHandle = `func (this *%s) Handle(c *gin.Context, ctx context.Context) htp.Response { // don't override
	cli := Cli%s(c)
	if cli == nil {
		return htp.RespRpcErr("", fmt.Errorf("no client(%s) available"))
	}
	rsp, err := cli.%s(ctx, &this.%s)
	if err != nil {
		return htp.RespRpcErr("", err)
	}
	return htp.RespOK("", rsp)
}

`

var emptySerInit = `package %s

/*******************************************************************************
Copyright:cloud
Author:cloudapex@126.com
Version:2.0
Date:%s
Description: 注册GRPC服务
# !!!文件自动生成，不要手动修改!!!
*******************************************************************************/
import (
	"gitee.com/cloudapex/ulern/rpc"
	"google.golang.org/grpc"
)

func init() {
	
}
`
var importPackge = `	"%s/services/%s/server/%s" // from %s
`
var registServer = `	rpc.RegistServer(%s,func(s *grpc.Server) { (&%s.Server{}).Init(s, %q) }) // from %s
`

var emptySerMain = `package %s

/*******************************************************************************
Copyright:cloud
Author:cloudapex@126.com
Version:2.0
Date:%s
Description: %s服务
# !文件自动创建，后续人工维护!
*******************************************************************************/

import (
	"context"
	"time"

	"gitee.com/cloudapex/ulern/rpc"
	"gitee.com/cloudapex/ulern/ulog"
	"%s/%s"
	"google.golang.org/grpc"
)

type Server struct {
	ulog.LogMe

	pb.%sServer
}

func (this *Server) Init(s *grpc.Server, name string) {
	pb.Register%sServer(s, this)
	this.LogMe.Init("GSER", func() string { return name })
}
`
